#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

#include "ysh.h"
#include "jc.h"
#include "pool.h"
#include "encap.h"
#include "error.h"

typedef struct HistoryItem {
    char item[LINE_BUF_SIZE];
    struct HistoryItem *prev;
    struct HistoryItem *next;
} HistoryItem;

int             jc_status;
char StatusString[][16] = {
    "JS_RUNNING", "JS_STOPPED", "JS_TERMINATED",
};

static Process      *process_pool, *current_process;                            // Process pool head.
static Job          *job_pool, *current_job;                                    // Job pool.
static HistoryItem  *history_pool, *current_item;                               // History pool.

typedef unsigned char element_t;
#define JID_POOL_NUM    (MAX_JOB_NUM >> (sizeof(element_t) * 3))
#define BIT_PER_ELEMENT (1 << (sizeof(element_t) * 3))
static element_t jid_pool[JID_POOL_NUM];
static int new_jid(void)
{
    int i, j;
    for (i = 0; i < JID_POOL_NUM; i++) {
        j = 0;
        while ((j < BIT_PER_ELEMENT) &&
               (((jid_pool[i] >> j) & 1) != 0)) {
            j++;
        }
        if (j != BIT_PER_ELEMENT) {
            jid_pool[i] |= (1 << j);
            return (BIT_PER_ELEMENT * i + j + 1);
        }
    }
    // Error.
    error_msg("Allocate job id failed.\n");
    return -1;
}

static void reclaim_jid(int jid)
{
    int i, j;

    i = jid / BIT_PER_ELEMENT;
    j = (jid - 1) % BIT_PER_ELEMENT;
    jid_pool[i] &= ~(1 << j);
}

void p_init(void)
{
    process_pool = (Process *)NULL;
    current_process = (Process *)NULL;
    job_pool = (Job *)NULL;
    current_job = (Job *)NULL;
    history_pool = (HistoryItem *)NULL;
    current_item = (HistoryItem *)NULL;
    jc_status = 0;
    memset(jid_pool, 0, sizeof(jid_pool));
}

Process *insert_p(pid_t pid, Job *job)
{
    if (process_pool == NULL) {
        process_pool = (Process *)ec_malloc(sizeof(Process));
        current_process = process_pool;
        current_process->prev = (Process *)NULL;
    }
    else {
        current_process->next = (Process *)ec_malloc(sizeof(Job));
        current_process->next->prev = current_process;
        current_process = current_process->next;
    }
    current_process->status = JS_RUNNING;
    current_process->pid = pid;
    current_process->job = job;
    current_process->next = NULL;
    return current_process;
}

Process *find_p(pid_t pid)
{
    Process *p = NULL;
    p = process_pool;
    while (p != NULL) {
        if (p->pid == pid) {
            return p;
        }
        else {
            p = p->next;
        }
    }
    return (Process *)NULL;
}

void delete_p(pid_t pid)
{
    Process *p = NULL;
    Job *job = NULL;

    p = find_p(pid);
    if (p != process_pool) {
        p->prev->next = p->next;
    }
    else {
        process_pool = p->next;
        if (process_pool != NULL) {
            process_pool->prev = NULL;
        }
    }

    if (p != current_process) {
        p->next->prev = p->prev;
    }
    else {
        current_process = p->prev;
        if (current_process != NULL) {
            current_process->next = NULL;
        }
    }

    job = p->job;
    if (job->jrpn > 0) {
        job->jrpn--;
    }
    if (job->jrpn == 0) {
        delete_job(job);
    }
    ec_free((void *)p);
}

Job *insert_job(void)
{
    if (job_pool == NULL) {
        job_pool = (Job *)ec_malloc(sizeof(Job));
        current_job = job_pool;
        current_job->prev = NULL;
    }
    else {
        current_job->next = (Job *)ec_malloc(sizeof(Job));
        current_job->next->prev = current_job;
        current_job = current_job->next;
    }
    current_job->jid = new_jid();
    current_job->jstatus = JS_RUNNING;
    current_job->next = NULL;
    return current_job;
}

Job *find_job(int jid)
{
    Job *p;
    p = job_pool;
    while (p != NULL) {
        if (p->jid == jid) {
            return p;
        }
    }
    return (Job *)NULL;
}

void delete_job(Job *job)
{
    if (job != job_pool) {
        job->prev->next = job->next;
    }
    else {
        job_pool = job->next;
        if (job_pool !=  NULL) {
            job_pool->prev = NULL;
        }
    }

    if (job != current_job) {
        job->next->prev = job->prev;
    }
    else {
        current_job = job->prev;
        if (current_job != NULL) {
            current_job->next = NULL;
        }
    }
    reclaim_jid(job->jid);
    ec_free((void *)job);
}

Bool pool_check_status(int jid, JobStatus js)
{
    Job *p;
    while (p != NULL) {
        if (p->jstatus != js && p->jid == jid) {
            return FALSE;
        }
        p = p->next;
    }
    return TRUE;
}

void insert_history(char *item)
{
    int len;
    len = strlen(item);
    if (history_pool == NULL) {
        history_pool = (HistoryItem *)ec_malloc(sizeof(HistoryItem));
        current_item = history_pool;
        current_item->prev = NULL;
    }
    else {
        current_item->next = (HistoryItem *)ec_malloc(sizeof(HistoryItem));
        current_item->next->prev = current_item;
        current_item = current_item->next;
    }
    strcpy(current_item->item, item);
    current_item->next = NULL;
}

void pool_print_history(void)
{
    int i = 1;
    HistoryItem *p;
    p = history_pool;
    while (p != NULL) {
        // No '\n' is needed here.
        printf("%-8d%s", i, p->item);
        i++;
        p = p->next;
    }
}

void pool_print_jobs(void)
{
    Job *p;

    p = job_pool;
    while (p != NULL) {
        printf("[%d]%4s%8s%4s%s", p->jid, "", StatusString[p->jstatus], "", p->jstring);
        p = p->next;
    }
}

void pool_kill_all(void)
{
    Job *p;

    p = job_pool;
    while (p != NULL) {
#ifdef DEBUG
        printf("%d\n", p->jid);
#endif
        ec_kill(-p->jgid, SIGKILL);
        p = p->next;
    }
}

